use std::{
    net::{Ipv4Addr, Ipv6Addr, SocketAddr},
    pin::Pin,
    task::{Context, Poll},
};

use hyper::server::{accept::Accept, conn::AddrIncoming};

pub struct CombinedIncoming {
    ipv4: AddrIncoming,
    ipv6: AddrIncoming,
}

impl CombinedIncoming {
    pub fn new(ipv4_port: u16, ipv6_port: u16) -> Self {
        let localhost_v4 = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), ipv4_port);
        let localhost_v6 = SocketAddr::new(Ipv6Addr::LOCALHOST.into(), ipv6_port);
        CombinedIncoming {
            ipv4: AddrIncoming::bind(&localhost_v4).unwrap(),
            ipv6: AddrIncoming::bind(&localhost_v6).unwrap(),
        }
    }
}

impl Accept for CombinedIncoming {
    type Conn = <AddrIncoming as Accept>::Conn;

    type Error = <AddrIncoming as Accept>::Error;

    fn poll_accept(
        mut self: Pin<&mut Self>,
        cx: &mut Context<'_>,
    ) -> Poll<Option<Result<Self::Conn, Self::Error>>> {
        if let Poll::Ready(Some(value)) = Pin::new(&mut self.ipv4).poll_accept(cx) {
            return Poll::Ready(Some(value));
        }

        if let Poll::Ready(Some(value)) = Pin::new(&mut self.ipv6).poll_accept(cx) {
            return Poll::Ready(Some(value));
        }

        Poll::Pending
    }
}
